package com.blog4j.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.blog4j.api.client.FeignUser;
import com.blog4j.api.vo.UserInfoVo;
import com.blog4j.auth.context.LoginContext;
import com.blog4j.auth.service.AuthService;
import com.blog4j.auth.utils.SecretUtils;
import com.blog4j.auth.vo.req.PhoneLoginReqVo;
import com.blog4j.auth.vo.req.WebLoginReqVo;
import com.blog4j.auth.vo.resp.AesKeyAndIvRespVo;
import com.blog4j.common.constants.CacheConstants;
import com.blog4j.common.enums.ErrorEnum;
import com.blog4j.common.enums.UserStatusEnum;
import com.blog4j.common.exception.Blog4jException;
import com.blog4j.common.utils.CommonUtil;
import com.blog4j.common.utils.RedisUtil;
import com.blog4j.common.utils.RsaUtil;
import com.blog4j.api.vo.EditUserLastLoginTimeReqVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 认证服务
 * @Create on : 2024/6/21 19:57
 **/
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class AuthServiceImpl implements AuthService {
    private final FeignUser feignUser;
    private final RedisUtil redisUtil;
    private static final String EXPIRED_CODE = "验证码已过期，请重新获取";

    /**
     * 登录
     *
     * @param loginContext 登录信息
     */
    @Override
    public void login(LoginContext loginContext) {
        // 判断验证码是否正确
        String checkResult = this.checkCaptcha(loginContext);
        if (StringUtils.isNotBlank(checkResult)) {
            log.error("验证码校验失败");
            throw new Blog4jException(ErrorEnum.INVALID_CAPTCHA_ERROR.getErrorCode(), checkResult);
        }

        // 远程调用user模块获取用户信息
        UserInfoVo userInfoVo1 = feignUser.getUserInfoByUserName(loginContext.getUserName());
        UserInfoVo userInfoVo2 = feignUser.getUserInfoByPhone(loginContext.getUserName());
        if (StringUtils.isBlank(userInfoVo1.getUserId()) && StringUtils.isBlank(userInfoVo2.getUserId())) {
            throw new Blog4jException(ErrorEnum.USER_NOT_EXIST_ERROR);
        }

        UserInfoVo userInfoVo = new UserInfoVo();
        if (StringUtils.isNotBlank(userInfoVo1.getUserId())) {
            Integer status = userInfoVo1.getStatus();
            if (Objects.equals(status, UserStatusEnum.LOCK.getCode())) {
                throw new Blog4jException(ErrorEnum.USER_LOCK_ERROR);
            }
            BeanUtils.copyProperties(userInfoVo1, userInfoVo);
        } else {
            Integer status = userInfoVo2.getStatus();
            if (Objects.equals(status, UserStatusEnum.LOCK.getCode())) {
                throw new Blog4jException(ErrorEnum.USER_LOCK_ERROR);
            }
            BeanUtils.copyProperties(userInfoVo2, userInfoVo);
        }
        String dbPassword = userInfoVo.getPassword();
        String reqPassword = SecretUtils.desEncrypt(loginContext.getPassword());
        String decrypt = RsaUtil.decrypt(dbPassword);
        if (!StringUtils.equals(decrypt, reqPassword)) {
            log.error("password error .");
            throw new Blog4jException(ErrorEnum.PASSWORD_ERROR);
        }

        StpUtil.login(userInfoVo.getUserId());
        SaTokenInfo saTokenInfo = StpUtil.getTokenInfo();
        loginContext.setSaTokenInfo(saTokenInfo);

        EditUserLastLoginTimeReqVo lastLoginTimeReqVo = EditUserLastLoginTimeReqVo.builder()
                .userId(userInfoVo.getUserId())
                .lastLoginTime(CommonUtil.getCurrentDateTime())
                .build();
        feignUser.updateUserLastLoginTime(lastLoginTimeReqVo);
    }

    /**
     * 退出登录
     *
     * @param userId 用户ID
     */
    @Override
    public void logout(String userId) {
        StpUtil.logout(userId);
    }

    /**
     * 获取AES前后端加解密的KEY和IV
     *
     * @return 前后端加解密的KEY和IV
     */
    @Override
    public AesKeyAndIvRespVo getAesKeyAndIv() {
        // TODO 调用系统服务获取
        return AesKeyAndIvRespVo.builder()
                .iv("63eeac68cf074c8c")
                .key("63eeac68cf074c8c")
                .build();
    }

    /**
     * 手机号码登录
     *
     * @param reqVo 登录信息
     * @return token信息
     */
    @Override
    public SaTokenInfo phoneLogin(PhoneLoginReqVo reqVo) {
        String phone = reqVo.getPhone();
        String code = reqVo.getCode();
        UserInfoVo userInfoVo = feignUser.getUserInfoByPhone(phone);
        if (StringUtils.isBlank(userInfoVo.getUserId())) {
            throw new Blog4jException(ErrorEnum.USER_NOT_EXIST_ERROR);
        }

        Integer status = userInfoVo.getStatus();
        if (Objects.equals(status, UserStatusEnum.LOCK.getCode())) {
            throw new Blog4jException(ErrorEnum.USER_LOCK_ERROR);
        }
        Object cacheCode = redisUtil.get(CacheConstants.PHONE_CODE_KEY + phone);
        if (Objects.isNull(cacheCode)) {
            throw new Blog4jException(ErrorEnum.INVALID_CAPTCHA_ERROR.getErrorCode(), EXPIRED_CODE);
        }
        if (!StringUtils.equalsIgnoreCase((String) cacheCode, code)) {
            throw new Blog4jException(ErrorEnum.INVALID_CAPTCHA_ERROR);
        }
        StpUtil.login(userInfoVo.getUserId());
        EditUserLastLoginTimeReqVo lastLoginTimeReqVo = EditUserLastLoginTimeReqVo.builder()
                .userId(userInfoVo.getUserId())
                .lastLoginTime(CommonUtil.getCurrentDateTime())
                .build();
        feignUser.updateUserLastLoginTime(lastLoginTimeReqVo);
        return StpUtil.getTokenInfo();
    }

    /**
     * web端登录
     *
     * @param reqVo 请求信息
     * @return 登录成功的信息
     */
    @Override
    public SaTokenInfo webLogin(WebLoginReqVo reqVo) {
        String password = reqVo.getPassword();
        String userName = reqVo.getUserName();

        UserInfoVo userInfoVo = feignUser.getUserInfoByUserName(userName);
        if (StringUtils.isBlank(userInfoVo.getUserId())) {
            throw new Blog4jException(ErrorEnum.USER_NOT_EXIST_ERROR);
        }

        String dbPassword = userInfoVo.getPassword();
        String decrypt = RsaUtil.decrypt(dbPassword);
        if (!StringUtils.equals(decrypt, password)) {
            log.error("password error .");
            throw new Blog4jException(ErrorEnum.PASSWORD_ERROR);
        }

        StpUtil.login(userInfoVo.getUserId());
        SaTokenInfo saTokenInfo = StpUtil.getTokenInfo();
        EditUserLastLoginTimeReqVo lastLoginTimeReqVo = EditUserLastLoginTimeReqVo.builder()
                .userId(userInfoVo.getUserId())
                .lastLoginTime(CommonUtil.getCurrentDateTime())
                .build();
        feignUser.updateUserLastLoginTime(lastLoginTimeReqVo);
        return saTokenInfo;
    }

    private String checkCaptcha(LoginContext loginContext) {
        String captcha = loginContext.getCaptcha();
        String uuid = loginContext.getUuid();

        Object val = redisUtil.get(uuid);
        if (Objects.isNull(val)) {
            return EXPIRED_CODE;
        }

        String captchaVal = (String) val;
        if (!StringUtils.equalsIgnoreCase(captcha, captchaVal)) {
            return "验证码非法";
        }

        redisUtil.del(uuid);
        return null;
    }
}
